﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.Mvc.Ajax;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web;

namespace OF.Controls
{
    /// <summary>
    /// 文件名称：MVC分页控件
    /// </summary>
    public class MVCPager
    {
        public MVCPager(string id, int pageSize, int recordCount)
        {
            if (string.IsNullOrEmpty(id))
                this.ID = "Pager_" + new Random().Next(100);
            else
                this.ID = id;
            this.pageSize = pageSize;
            this.RecordCount = recordCount;
        }

        private string actionName, controllerName;
        private RouteValueDictionary routeValues;
        private const string ScriptPageIndexName = "*_MvcPager_PageIndex_*";

        #region 字段

        //数据
        private int currentIndex = 1;
        private int pageSize = 10;
        private int recordCount = 1;
        private int pageCount = 1;
        //行为
        private bool showTotalPageText = true;
        private bool alwaysShow;
        private bool alwaysShowFirstLastPageNumber = true;
        private bool showSelect = true;
        private bool showFirstLast = true;
        private bool showPrevNext = true;
        private int sidePageCount = 3;
        //导航
        private string firstPageText = "首页";
        private string prevPageText = "上一页";
        private string nextPageText = "下一页";
        private string lastPageText = "尾页";
        private string totalPageText = "(当前 c/t 页 每页 i/s 条)";
        //外观
        private string cssClass = "ofpage";
        private string currentPageButtonClass = "page-cur";
        private string firstButtonsClass = "page-start";
        private string lastButtonsClass = "page-end";
        private string nextButtonsClass = "page-next";
        private string prevButtonsClass = "page-prev";
        private string totalPageTextClass = "page-skip";
        private string pageIndexParameterName = "p";

        #endregion

        #region 属性

        /// <summary>
        /// 控件ID
        /// </summary>
        public string ID { get; set; }

        #region 数据属性

        /// <summary>
        /// 当前页码
        /// </summary>
        public int CurrentIndex
        {
            get
            {
                string cp = HttpContext.Current.Request.QueryString[pageIndexParameterName];
                int.TryParse(cp, out currentIndex);
                if (currentIndex < 1)
                    currentIndex = 1;
                return currentIndex;
            }
            set
            {
                if (value > 0)
                    currentIndex = value;
            }
        }
        /// <summary>
        /// 页面大小
        /// </summary>
        public int PageSize
        {
            get { return pageSize; }
            set
            {
                if (value > 0)
                    pageSize = value;
            }
        }
        /// <summary>
        /// 总页数
        /// </summary>
        public int PageCount
        {
            get { return pageCount; }
        }
        /// <summary>
        /// 总记录数
        /// </summary>
        public int RecordCount
        {
            get { return recordCount; }
            set
            {
                if (value > 0)
                {
                    recordCount = value;
                    pageCount = recordCount / pageSize;
                    if (recordCount % pageSize > 0)
                    {
                        pageCount += 1;
                    }
                }
            }
        }
        /// <summary>
        /// 页码参数名
        /// </summary>
        public string PageIndexParameterName
        {
            get { return pageIndexParameterName; }
            set { pageIndexParameterName = value; }
        }

        #endregion

        #region 行为属性

        /// <summary>
        /// 是否显示当前总页数，显示方式：(共 2/5 页)
        /// </summary>
        public bool ShowTotalPageText
        {
            get { return showTotalPageText; }
            set { showTotalPageText = value; }
        }

        /// <summary>
        /// 是否总是显示分页控件
        /// </summary>        
        public bool AlwaysShow
        {
            get { return alwaysShow; }
            set { alwaysShow = value; }
        }

        /// <summary>
        /// 是否总是显示首页和尾页的页码链接
        /// </summary>
        public bool AlwaysShowFirstLastPageNumber
        {
            get { return alwaysShowFirstLastPageNumber; }
            set { alwaysShowFirstLastPageNumber = value; }
        }

        /// <summary>
        /// 是否显示下拉框
        /// </summary>
        public bool ShowSelect
        {
            get { return showSelect; }
            set { showSelect = value; }
        }

        /// <summary>
        /// 是否显示首页和尾页链接
        /// </summary>
        public bool ShowFirstLast
        {
            get { return showFirstLast; }
            set { showFirstLast = value; }
        }

        /// <summary>
        /// 是否显示上一页和下一页链接
        /// </summary>
        public bool ShowPrevNext
        {
            get { return showPrevNext; }
            set { showPrevNext = value; }
        }

        /// <summary>
        /// 当前页前后显示的页码数,范围1-5
        /// </summary>
        public int SidePageCount
        {
            get { return sidePageCount; }
            set
            {
                if (value < 0)
                    sidePageCount = 0;
                else if (value > 5)
                    sidePageCount = 5;
                else
                    sidePageCount = value;
            }
        }
        #endregion

        #region 导航属性

        /// <summary>
        /// 总页码显示格式字符串,默认:(当前 c/t 页 每页 i/s 条)
        ///     当前页用 c 代替，总页码用 t 代替
        ///     每页显示数用 i 代替,总记录数用 s 代替，不区分大小写
        /// </summary>
        public string TotalPageText
        {
            get { return totalPageText; }
            set { totalPageText = value; }
        }
        /// <summary>
        /// 首页链接显示的文字
        /// </summary>
        public string FirstPageText
        {
            get { return firstPageText; }
            set { firstPageText = value; }
        }

        /// <summary>
        /// 尾页链接显示的文字
        /// </summary>
        public string LastPageText
        {
            get { return lastPageText; }
            set { lastPageText = value; }
        }

        /// <summary>
        /// 上一页链接显示的文字
        /// </summary>
        public string PrevPageText
        {
            get { return prevPageText; }
            set { prevPageText = value; }
        }

        /// <summary>
        /// 下一页链接显示的文字
        /// </summary>
        public string NextPageText
        {
            get { return nextPageText; }
            set { nextPageText = value; }
        }

        #endregion

        #region 外观属性

        /// <summary>
        /// 应用于分页控件上的 CSS 类名
        /// </summary>
        public string CssClass
        {
            get { return cssClass; }
            set { cssClass = value; }
        }

        /// <summary>
        /// 应用于首页链接的 CSS 类名
        /// </summary>
        public string FirstButtonsClass
        {
            get { return firstButtonsClass; }
            set { firstButtonsClass = value; }
        }

        /// <summary>
        /// 应用于尾页链接的 CSS 类名
        /// </summary>
        public string LastButtonsClass
        {
            get { return lastButtonsClass; }
            set { lastButtonsClass = value; }
        }

        /// <summary>
        /// 应用于下一页链接的 CSS 类名
        /// </summary>
        public string NextButtonsClass
        {
            get { return nextButtonsClass; }
            set { nextButtonsClass = value; }
        }

        /// <summary>
        /// 应用于上一页链接的 CSS 类名
        /// </summary>
        public string PrevButtonsClass
        {
            get { return prevButtonsClass; }
            set { prevButtonsClass = value; }
        }

        /// <summary>
        /// 应用于当前页链接的 CSS 类名
        /// </summary>
        public string CurrentPageButtonClass
        {
            get { return currentPageButtonClass; }
            set { currentPageButtonClass = value; }
        }

        /// <summary>
        /// 应用于总页码字符串的 CSS 类名
        /// </summary>
        public string TotalPageTextClass
        {
            get { return totalPageTextClass; }
            set { totalPageTextClass = value; }
        }

        #endregion

        #endregion

        #region 方法

        /// <summary>
        /// 设置导航文字
        /// </summary>
        /// <param name="firstPageText">首页链接显示的文字</param>
        /// <param name="lastPageText">尾页链接显示的文字</param>
        /// <param name="prevPageText">上一页链接显示的文字</param>
        /// <param name="nextPageText">下一页链接显示的文字</param>
        /// <param name="totalPageText">总页码显示格式字符串</param>
        public void SetNavigation(string firstPageText, string lastPageText, string prevPageText, string nextPageText, string totalPageText)
        {
            this.firstPageText = firstPageText;
            this.lastPageText = lastPageText;
            this.prevPageText = prevPageText;
            this.nextPageText = nextPageText;
            this.totalPageText = totalPageText;
        }

        /// <summary>
        /// 设置行为
        /// </summary>
        /// <param name="alwaysShow">是否总是显示分页控件</param>
        /// <param name="showTotalPageText">是否显示当前总页数，显示方式类似：(共 2/5 页)</param>
        /// <param name="alwaysShowFirstLastPageNumber">是否总是显示首页和尾页的页码链接</param>
        /// <param name="showFirstLast">是否显示首页和尾页链接</param>
        /// <param name="showPrevNext">是否显示上一页和下一页链接</param>
        /// <param name="showSelect">是否显示下拉框</param>
        public void SetBehavior(bool alwaysShow, bool showTotalPageText, bool alwaysShowFirstLastPageNumber, bool showFirstLast, bool showPrevNext, bool showSelect)
        {
            this.alwaysShow = alwaysShow;
            this.alwaysShowFirstLastPageNumber = alwaysShowFirstLastPageNumber;
            this.showFirstLast = showFirstLast;
            this.showPrevNext = showPrevNext;
            this.showSelect = showSelect;
            this.showTotalPageText = showTotalPageText;
        }

        /// <summary>
        /// 获取总页码显示字符串
        /// </summary>
        /// <returns>总页码显示字符串</returns>
        private string GetTotlePageText()
        {
            string str = totalPageText;
            str = Regex.Replace(str, @"[c|C]+", currentIndex.ToString());
            str = Regex.Replace(str, @"[t|T]+", pageCount.ToString());
            str = Regex.Replace(str, @"[i|I]+", pageSize.ToString());
            str = Regex.Replace(str, @"[s|S]+", recordCount.ToString());
            return str;
        }

        private string GetHref(UrlHelper url, object index, object text, string css, bool isDisable)
        {
            if (isDisable)
                return string.Format("<span class=\"{0}\">{1}</span>", css, text);
            return GetHref(url, index, text, css);
        }

        private string GetHref(UrlHelper url, object index, object text, string css)
        {
            if (!routeValues.ContainsKey(pageIndexParameterName))
                routeValues.Add(pageIndexParameterName, index);
            else
                routeValues[pageIndexParameterName] = index;
            string href = url.Action(actionName, controllerName, routeValues);
            return string.Format("<a href=\"{0}\" class=\"{1}\">{2}</a>", href, css, text);
        }

        private string GetAjaxHref(AjaxHelper ajax, object index, object text, string css, AjaxOptions ajaxOptions, bool isDisable)
        {
            if (isDisable)
                return string.Format("<span class=\"{0}\">{1}</span>", css, text);
            return GetAjaxHref(ajax, index, text, css, ajaxOptions);
        }

        private string GetAjaxHref(AjaxHelper ajax, object index, object text, string css, AjaxOptions ajaxOptions)
        {
            if (!routeValues.ContainsKey(pageIndexParameterName))
                routeValues.Add(pageIndexParameterName, index);
            else
                routeValues[pageIndexParameterName] = index;
            var htmlAttrs = new Dictionary<string, object> { { "class", css } };
            return ajax.ActionLink(text.ToString(), actionName, routeValues, ajaxOptions, htmlAttrs).ToString();
        }

        internal MvcHtmlString RenderHtmlPager(HtmlHelper html)
        {
            if (html != null)
            {
                if (string.IsNullOrEmpty(actionName))
                {
                    actionName = (string)html.ViewContext.RouteData.Values["action"];
                }
                if (string.IsNullOrEmpty(controllerName))
                {
                    controllerName = (string)html.ViewContext.RouteData.Values["controller"];
                }
                routeValues = html.ViewContext.RouteData.Values;
            }
            var htmlString = new StringBuilder();
            if (html != null)
            {
                var url = new UrlHelper(html.ViewContext.RequestContext);
                if (AlwaysShow || PageCount > 1)
                {
                    int i = currentIndex - SidePageCount;
                    int max = currentIndex + SidePageCount;
                    htmlString.AppendFormat("<div id=\"{0}\" class=\"{1}\">", ID, CssClass);
                    if (ShowTotalPageText)
                    {
                        htmlString.AppendFormat("<span class=\"{0}\">{1}</span>", TotalPageTextClass, GetTotlePageText());
                    }
                    if (ShowFirstLast)
                    {
                        htmlString.Append(GetHref(url, 1, firstPageText, firstButtonsClass, currentIndex == 1));
                    }
                    if (ShowPrevNext)
                    {
                        htmlString.Append(GetHref(url, currentIndex - 1, prevPageText, prevButtonsClass, currentIndex == 1));
                    }
                    if (i == 2)
                    {
                        i = 1;
                    }
                    if (AlwaysShowFirstLastPageNumber && i > 1)
                    {
                        htmlString.Append(GetHref(url, 1, 1, "", currentIndex == 1));
                    }
                    if (i > 2)
                    {
                        htmlString.Append(GetHref(url, i - 1, i == 3 ? "2" : "...", ""));
                    }
                    for (; i < currentIndex; i++)
                    {
                        if (i > 0)
                        {
                            htmlString.Append(GetHref(url, i, i, ""));
                        }
                    }
                    htmlString.Append(GetHref(url, currentIndex, currentIndex, currentPageButtonClass, true));
                    if (max == PageCount - 1)
                    {
                        max = PageCount;
                    }
                    for (i = currentIndex + 1; i <= max; i++)
                    {
                        if (i <= PageCount)
                        {
                            htmlString.Append(GetHref(url, i, i, ""));
                        }
                    }
                    if (i < PageCount)
                    {
                        //htmlString.Append(GetHref(url, i, (i == PageCount - 1 ? (pageCount - 1).ToString() : "..."), "", i == PageCount - 1));
                        htmlString.Append(GetHref(url, i, (i == PageCount - 1 ? (pageCount - 1).ToString() : "..."), ""));//edit by frank 2013/4/15
                    }
                    if (AlwaysShowFirstLastPageNumber && i < PageCount)
                    {
                        htmlString.Append(GetHref(url, PageCount, PageCount, "", currentIndex == PageCount));
                    }
                    if (ShowPrevNext)
                    {
                        htmlString.Append(GetHref(url, currentIndex + 1, NextPageText, nextButtonsClass, currentIndex == PageCount));
                    }
                    if (ShowFirstLast)
                    {
                        htmlString.Append(GetHref(url, PageCount, LastPageText, lastButtonsClass, currentIndex == PageCount));
                    }
                    if (PageCount > 1 && ShowSelect)
                    {
                        if (!routeValues.ContainsKey(pageIndexParameterName))
                            routeValues.Add(pageIndexParameterName, ScriptPageIndexName);
                        else
                            routeValues[pageIndexParameterName] = ScriptPageIndexName;
                        string href = url.Action(actionName, controllerName, routeValues);
                        htmlString.AppendFormat("<script type=\"text/javascript\">" + "function Change{0}() {{" + "   var select=document.getElementById('{0}_select').options;" + "   var i=select[select.selectedIndex].value;" + "   window.location.href=\"{1}\".replace('{2}',i);" + "}}" + "</script>", ID, href, ScriptPageIndexName);
                        htmlString.AppendFormat("<select name=\"{0}_select\" id=\"{0}_select\" onchange=\"Change{0}()\">", ID);

                        for (i = 1; i <= PageCount; i++)
                        {
                            htmlString.AppendFormat("<option value=\"{0}\" {1}>{0}</option>", i, (i == currentIndex ? "selected = selected" : ""));
                        }
                        htmlString.Append("</select>");
                    }
                    htmlString.Append("</div>");
                }
            }

            return MvcHtmlString.Create(htmlString.ToString());
        }

        internal MvcHtmlString RenderAjaxPager(AjaxHelper ajax, AjaxOptions ajaxOptions)
        {
            if (ajax != null)
            {
                if (string.IsNullOrEmpty(actionName))
                {
                    actionName = (string)ajax.ViewContext.RouteData.Values["action"];
                }
                if (string.IsNullOrEmpty(controllerName))
                {
                    controllerName = (string)ajax.ViewContext.RouteData.Values["controller"];
                }
                routeValues = ajax.ViewContext.RouteData.Values;
            }

            var html = new StringBuilder();
            if (AlwaysShow || PageCount > 1)
            {
                int i = currentIndex - SidePageCount;
                int max = currentIndex + SidePageCount;
                html.AppendFormat("<div id=\"{0}\" class=\"{1}\">", ID, CssClass);
                if (ShowTotalPageText)
                {
                    html.AppendFormat("<span class=\"{0}\">{1}</span>", TotalPageTextClass, GetTotlePageText());
                }
                if (ShowFirstLast)
                {
                    html.Append(GetAjaxHref(ajax, 1, firstPageText, firstButtonsClass, ajaxOptions, currentIndex == 1));
                }
                if (ShowPrevNext)
                {
                    html.Append(GetAjaxHref(ajax, currentIndex - 1, prevPageText, prevButtonsClass, ajaxOptions, currentIndex == 1));
                }
                if (i == 2)
                {
                    i = 1;
                }
                if (AlwaysShowFirstLastPageNumber && i > 1)
                {
                    html.Append(GetAjaxHref(ajax, 1, "1", "", ajaxOptions, currentIndex == 1));
                }
                if (i > 2)
                {
                    html.Append(GetAjaxHref(ajax, i - 1, i == 3 ? "2" : "...", "", ajaxOptions));
                }
                for (; i < currentIndex; i++)
                {
                    if (i > 0)
                    {
                        html.Append(GetAjaxHref(ajax, i, i, "", ajaxOptions));
                    }
                }
                html.Append(GetAjaxHref(ajax, currentIndex, currentIndex, currentPageButtonClass, ajaxOptions, true));
                if (max == PageCount - 1)
                {
                    max = PageCount;
                }
                for (i = currentIndex + 1; i <= max; i++)
                {
                    if (i <= PageCount)
                    {
                        html.Append(GetAjaxHref(ajax, i, i, "", ajaxOptions));
                    }
                }
                if (i < PageCount)
                {
                    html.Append(GetAjaxHref(ajax, i, (i == PageCount - 1 ? (pageCount - 1).ToString() : "..."), "", ajaxOptions, i == PageCount - 1));
                }
                if (AlwaysShowFirstLastPageNumber && i < PageCount)
                {
                    html.Append(GetAjaxHref(ajax, PageCount, PageCount, "", ajaxOptions, currentIndex == PageCount));
                }
                if (ShowPrevNext)
                {
                    html.Append(GetAjaxHref(ajax, currentIndex + 1, NextPageText, nextButtonsClass, ajaxOptions, currentIndex == PageCount));
                }
                if (ShowFirstLast)
                {
                    html.Append(GetAjaxHref(ajax, PageCount, LastPageText, lastButtonsClass, ajaxOptions, currentIndex == PageCount));
                }
                if (PageCount > 1 && ShowSelect)
                {
                    var goToPageScript = string.Format("function Change{0}(_pib,_mp){{var pageIndex;if(_pib.tagName==\"SELECT\"){{pageIndex=_pib.options[_pib.selectedIndex].value;}}var _hl=document.getElementById('{0}link').childNodes[0];var _lh=_hl.href;_hl.href=_lh.replace('{1}',pageIndex);if(_hl.click){{_hl.click();}}else{{var evt=document.createEvent('MouseEvents');evt.initEvent('click',true,true);_hl.dispatchEvent(evt);}}_hl.href=_lh;}}", ID, ScriptPageIndexName);
                    html.AppendFormat("<script type=\"text/javascript\">{0}</script>", goToPageScript);
                    html.AppendFormat("<select name=\"{0}_select\" id=\"{0}_select\" onchange=\"Change{0}(document.getElementById('{0}_select'),{1})\">", ID, pageCount);

                    for (i = 1; i <= PageCount; i++)
                    {
                        html.AppendFormat("<option value=\"{0}\" {1}>{0}</option>", i, (i == currentIndex ? "selected = selected" : ""));
                    }
                    html.Append("</select>");
                    html.AppendFormat("<span id=\"{0}link\" style=\"display:none;width:0px;height:0px\">{1}</span>", ID, GetAjaxHref(ajax, ScriptPageIndexName, 0, "", ajaxOptions));
                }
                html.Append("</div>");
            }

            return MvcHtmlString.Create(html.ToString());
        }

        #endregion
    }

    /// <summary>
    /// 静态扩展方法,实现MVC分页
    /// </summary>
    public static class PagerHelper
    {
        /// <summary>
        /// 分页控件
        /// </summary>
        /// <param name="html"></param>
        /// <param name="pager">分页实体</param>
        /// <returns></returns>
        public static MvcHtmlString Pager(this HtmlHelper html, MVCPager pager)
        {
            return pager.RenderHtmlPager(html);
        }

        /// <summary>
        /// AJAX分页方法
        /// </summary>
        /// <param name="ajax"></param>
        /// <param name="pager">分页实体</param>
        /// <param name="ajaxOptions"></param>
        /// <returns></returns>
        public static MvcHtmlString Pager(this AjaxHelper ajax, MVCPager pager, AjaxOptions ajaxOptions)
        {
            return pager.RenderAjaxPager(ajax, ajaxOptions);
        }

        /*
                private static string GetCurrentRouteValues(HttpRequestBase request, MVCPager pager)
                {
                    var rq = request.QueryString;
                    string q = string.Empty;
                    if (rq != null && rq.Count > 0)
                    {
                        var invalidParams = new[] { "x-requested-with", "xmlhttprequest", pager.PageIndexParameterName };
                        foreach (string key in rq.Keys)
                        {
                            if (!string.IsNullOrEmpty(key) && Array.IndexOf(invalidParams, key.ToLower()) < 0)
                            {
                                q += string.Format("&{0}={1}", key, rq[key]);
                            }
                        }
                    }
                    if (q.Length > 0)
                        q = "?" + q.Substring(1);
                    if (request.Url != null) return request.Url.AbsolutePath + q;
                    return null;
                }
        */
    }
}
